﻿using Avalonia.Controls;
using LightCAD.Core;
using LightCAD.Core.Elements;
using LightCAD.Drawing;
using LightCAD.Runtime;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QdArch
{
    public partial class DoorAction : ElementAction
    {
        private ElementInputer ElementInputer { get; set; }

        public DoorAction() { }
        public DoorAction(DocumentRuntime docRt) : base(docRt)
        {
            docRt.CommandCtrl.WriteInfo("命令：Door");
        }

        internal static void Initilize()
        {
            ArchActions.DoorAction = new DoorAction();
            LcDocument.ElementActions.Add(ArchElementType.Door, ArchActions.DoorAction);
        }

        public async void ExecCreate(string[] args = null)
        {
            //1. 选择元素
            //2. 选择门窗类型
            this.ElementInputer = new ElementInputer(this.docRt);
        Step0:
            var result = await this.ElementInputer.Execute("选择墙体:");
            if (result == null || result.Value == null || !(result.Value is QdWall))
            {
                goto Step0;
            }
        Step1:
            string doorName = "Single45";
            var instP = (Vector2d)result.Extent;
            var wall = (QdWall)result.Value;
            var wallLine = wall.BaseLine;
            var isLineRight = IsRight(wallLine.Start, wallLine.End, instP);
            var door = docRt.Document.Components.Find(ArchCategory.Door.ToString(), doorName) as QdDoor;
            if (door == null)
            {
                door = new QdDoor(DoorCategory.SwingDoor.ToString(), doorName);
                docRt.Document.Components.Add(door);
            }

            var pset = QdDoor.ParamsDefinition.New();
            pset["Thickness"] = 50.0;
            pset["Width"] = 900.0;
            pset["Height"] = 2100.0;
            pset["Bottom"] = 0;
            pset["IsLeft"] = false;
            pset["IsNear"] = isLineRight;

            var projectP = wallLine.Projection(instP);

            var doorRef = this.docRt.Document.CreateObject<QdDoorRef>();
            doorRef.Parameters = pset;
            doorRef.InsertPoint = projectP;
            doorRef.Door = door;
            doorRef.Wall = wall;

            var wallLineDir = wallLine.Dir;
            var offset = wallLineDir * (doorRef.Width / 2);
            doorRef.StartPoint = doorRef.InsertPoint - offset;
            doorRef.EndPoint = doorRef.InsertPoint + offset;

            doorRef.ResetControlPoint();

            wall.AddLcComponent(doorRef, "Door", AssociateType.Cross);

            this.ElementInputer = null;
            this.EndCreating();
            this.DrawDoor(doorRef);
        }

        public override void Draw(SKCanvas canvas, LcElement element, Vector2d offset)
        {
            var doorRef = element as QdDoorRef;
            //var shape2d = doorRef.Door.Shape2D;
            var wallLine = doorRef.Wall.BaseLine;

            var angle = wallLine.Dir.Angle();
            var matrix = doorRef.Matrix;

            //var shape = QdDoor.ShapeProvider.Shapes.GetShape(door.Name);
            var curves = doorRef.ShapeCurves;

            var pen = this.GetDrawPen(element);
            if (pen == Constants.defaultPen)
            {
                //pen = new SKPaint { Color = new SKColor(element.GetColorValue()), IsStroke = true };
                pen = new SKPaint { Color = SKColors.Red, IsStroke = true };
            }

            var box2d = doorRef.BoundingBox;
            var rectPen = new SKPaint { Color = SKColors.Green, IsStroke = true };

            var lt = this.vportRt.ConvertWcsToScr(box2d.LeftTop).ToSKPoint();
            var rt = this.vportRt.ConvertWcsToScr(box2d.RightTop).ToSKPoint();
            var lb = this.vportRt.ConvertWcsToScr(box2d.LeftBottom).ToSKPoint();
            var rb = this.vportRt.ConvertWcsToScr(box2d.RightBottom).ToSKPoint();
            //canvas.DrawLine(lt, rt, rectPen);
            //canvas.DrawLine(rt, rb, rectPen);
            //canvas.DrawLine(rb, lb, rectPen);
            //canvas.DrawLine(lb, lt, rectPen);
            DrawDoorCurves(canvas, curves, matrix, angle, pen);
        }

        public void DrawDoorCurves(SKCanvas canvas, List<ICurve2d> curves, Matrix3d matrix, double rotateAngle, SKPaint pen)
        {

            foreach (var curve in curves)
            {
                switch (curve.Type)
                {
                    case Curve2dType.Line2d:
                        {
                            var line = curve as Line2d;
                            var wsp = matrix.MultiplyPoint(line.Start);
                            var wep = matrix.MultiplyPoint(line.End);

                            var ssp = this.vportRt.ConvertWcsToScr(wsp).ToSKPoint();
                            var sep = this.vportRt.ConvertWcsToScr(wep).ToSKPoint();
                            canvas.DrawLine(ssp, sep, pen);
                            break;
                        }
                    case Curve2dType.Arc2d:
                        {
                            var arc = curve as Arc2d;

                            var wcenter = matrix.MultiplyPoint(arc.Center);
                            var scenter = this.vportRt.ConvertWcsToScr(wcenter).ToSKPoint();

                            var wrect = new Box2d(wcenter.X - arc.Radius, wcenter.Y - arc.Radius, arc.Radius * 2, arc.Radius * 2);
                            var plt = this.vportRt.ConvertWcsToScr(wrect.LeftTop);
                            var prb = this.vportRt.ConvertWcsToScr(wrect.RightBottom);
                            var scrRect = new SKRect((float)plt.X, (float)plt.Y, (float)prb.X, (float)prb.Y);
                            var sweep = arc.EndAngle - arc.StartAngle;
                            //Skia绘制圆弧是按照顺时针的，与数学逻辑相反
                            canvas.DrawArc(scrRect, -(float)(arc.EndAngle + Utils.RadianToDegree(rotateAngle)), (float)sweep, false, pen);
                            break;
                        }
                    case Curve2dType.Polyline2d:
                        {
                            var pline = curve as Polyline2d;
                            var scrPoints = pline.Points.Select((p) => matrix.MultiplyPoint(p).ToSKPoint()).ToArray();
                            canvas.DrawPoints(SKPointMode.Polygon, scrPoints, pen);
                            break;
                        }
                    case Curve2dType.Polygon2d:
                        {
                            var polygon = curve as Polygon2d;
                            var scrPoints = polygon.Points.Select((p) => matrix.MultiplyPoint(p).ToSKPoint()).ToList();
                            scrPoints.Add(scrPoints[0]);
                            canvas.DrawPoints(SKPointMode.Polygon, scrPoints.ToArray(), pen);
                            break;
                        }
                    default:
                        break;
                }
            }
        }


        public void DrawDoor(QdDoorRef doorRef)
        {
            this.docRt.Document.ModelSpace.InsertElement(doorRef);
            this.docRt.Action.ClearSelects();
            DocumentManager.CurrentRecorder.EndAction();
        }

        #region Grip
        private QdDoorRef _doorRef;
        private string _gripName;
        private Vector2d _position;

        public override ControlGrip[] GetControlGrips(LcElement element)
        {
            var doorRef = element as QdDoorRef;
            var grips = new List<ControlGrip>();

            var gripCenter = new ControlGrip
            {
                Element = doorRef,
                Name = "Center",
                Position = doorRef.InsertPoint
            };
            grips.Add(gripCenter);

            var wallLine = doorRef.Wall.BaseLine;
            var gripLeft = new ControlGrip
            {
                Element = doorRef,
                Name = "Left",
                Position = doorRef.StartPoint
            };
            grips.Add(gripLeft);

            var gripRight = new ControlGrip
            {
                Element = doorRef,
                Name = "Right",
                Position = doorRef.EndPoint
            };
            grips.Add(gripRight);

            
            var gripOpenDir = new ControlGrip
            {
                Element = doorRef,
                Name = "Control",
                Position = doorRef.ControlPoint
            };
            grips.Add(gripOpenDir);

            return grips.ToArray();
        }

        public override void SetDragGrip(LcElement element, string gripName, Vector2d position, bool isEnd)
        {
            var doorRef = element as QdDoorRef;
            _doorRef = doorRef;
            if (!isEnd)
            {
                _gripName = gripName;
                _position = position;
            }
            else
            {
                bool needReset = false; //墙体上是否需要重新打断
                if (gripName == "Center")
                {
                    position = _doorRef.Wall.BaseLine.Projection(position);
                    var offset = position - doorRef.InsertPoint;
                    doorRef.InsertPoint = position;
                    doorRef.StartPoint += offset;
                    doorRef.EndPoint += offset;
                    doorRef.ControlPoint += offset;
                    needReset = true;
                }
                else if(gripName == "Right")
                {
                    position = doorRef.Wall.BaseLine.Projection(_position);
                    var offset = (_position - doorRef.EndPoint) / 2;

                    doorRef.InsertPoint += offset;
                    doorRef.EndPoint = _position;
                    doorRef.ControlPoint += offset;

                    doorRef.Width = Vector2d.Distance(doorRef.EndPoint, doorRef.StartPoint);
                    doorRef.ResetShapeCurves();
                    doorRef.ResetControlPoint();
                    needReset = true;
                }
                else if (gripName == "Left")
                {
                    position = doorRef.Wall.BaseLine.Projection(_position);
                    var offset = (_position - doorRef.StartPoint) / 2;

                    doorRef.InsertPoint += offset;
                    doorRef.StartPoint = _position;
                    doorRef.ControlPoint += offset;

                    doorRef.Width = Vector2d.Distance(doorRef.EndPoint, doorRef.StartPoint);
                    doorRef.ResetShapeCurves();
                    doorRef.ResetControlPoint();
                    needReset = true;
                }
                else if (_gripName == "Control")
                {
                    bool needCurveReset = false;
                    var wallLine = doorRef.Wall.BaseLine;
                    var isNear = IsRight(wallLine.Start, wallLine.End, _position);
                    if (doorRef.IsNear != isNear)
                    {
                        doorRef.IsNear = isNear;
                        var mirrorMatrix = Matrix3d.Mirror(wallLine.Start, wallLine.End);
                        var controlP = mirrorMatrix.MultiplyPoint(doorRef.ControlPoint);
                        doorRef.ControlPoint = controlP;
                        needCurveReset = true;
                    }

                    var dir = wallLine.Dir;
                    var rotateDir = Vector2d.RotateInRadian(dir, Math.PI / 2);
                    var newInsertP = doorRef.InsertPoint + (rotateDir * doorRef.Width);

                    var isLeft = !IsRight(doorRef.InsertPoint, newInsertP, _position);
                    if (doorRef.IsLeft != isLeft)
                    {
                        doorRef.IsLeft = isLeft;
                        var mirrorMatrix = Matrix3d.Mirror(doorRef.InsertPoint, newInsertP);
                        var controlP = mirrorMatrix.MultiplyPoint(doorRef.ControlPoint);
                        doorRef.ControlPoint = controlP;
                        needCurveReset = true;
                    }
                    if (needCurveReset)
                    {
                        doorRef.ResetShapeCurves();
                    }
                }
                if (needReset)
                {
                    _doorRef.Wall.UpdateLcComponent(_doorRef, "Door", AssociateType.Cross);
                }
            }
        }

        public override void DrawDragGrip(SKCanvas canvas)
        {
            if (_doorRef == null)
                return;

            var doorRef = (QdDoorRef)_doorRef.Clone();
            var wallLine = doorRef.Wall.BaseLine;
            if (_gripName == "Center")
            {
                //_position = _doorRef.Wall.BaseLine.Projection(_position);
                var offset = _position - doorRef.InsertPoint;
                doorRef.InsertPoint = _position;
                doorRef.StartPoint += offset;
                doorRef.EndPoint += offset;
                doorRef.ControlPoint += offset;
            }
            else if (_gripName == "Right")
            {
                _position = doorRef.Wall.BaseLine.Projection(_position);
                var offset = (_position - doorRef.EndPoint) / 2;

                doorRef.InsertPoint += offset;
                doorRef.EndPoint = _position;
                doorRef.ControlPoint += offset;

                doorRef.Width = Vector2d.Distance(doorRef.EndPoint, doorRef.StartPoint);
                doorRef.ResetShapeCurves();
            }
            else if (_gripName == "Left")
            {
                _position = doorRef.Wall.BaseLine.Projection(_position);
                var offset = (_position - doorRef.StartPoint) / 2;

                doorRef.InsertPoint += offset;
                doorRef.StartPoint = _position;
                doorRef.ControlPoint += offset;

                doorRef.Width = Vector2d.Distance(doorRef.EndPoint, doorRef.StartPoint);
            }
            else if(_gripName == "Control")
            {
                bool needReset = false;
                var isNear = IsRight(wallLine.Start, wallLine.End, _position);
                if (doorRef.IsNear != isNear)
                {
                    doorRef.IsNear = isNear;
                    var mirrorMatrix = Matrix3d.Mirror(wallLine.Start, wallLine.End);
                    var controlP = mirrorMatrix.MultiplyPoint(doorRef.ControlPoint);
                    doorRef.ControlPoint = controlP;
                    needReset = true;
                }

                var dir = wallLine.Dir;
                var rotateDir = Vector2d.RotateInRadian(dir, Math.PI / 2);
                var newInsertP = doorRef.InsertPoint + (rotateDir * doorRef.Width);

                var isLeft = !IsRight(doorRef.InsertPoint, newInsertP, _position);
                if (doorRef.IsLeft != isLeft)
                {
                    doorRef.IsLeft = isLeft;
                    var mirrorMatrix = Matrix3d.Mirror(doorRef.InsertPoint, newInsertP);
                    var controlP = mirrorMatrix.MultiplyPoint(doorRef.ControlPoint);
                    doorRef.ControlPoint = controlP;
                    needReset = true;
                }
                if (needReset)
                {
                    doorRef.ResetShapeCurves();
                }
            }
            //var shape2d = doorRef.Door.Shape2D;

            var angle = wallLine.Dir.Angle();
            var matrix = doorRef.Matrix;

            //var shape = QdDoor.ShapeProvider.Shapes.GetShape(door.Name);
            var curves = doorRef.ShapeCurves;
            DrawDoorCurves(canvas, curves, matrix, angle, Constants.draggingPen);
        }
        #endregion

        public bool IsRight(Vector2d start, Vector2d end, Vector2d p)
        {
            //Vsub(D,A,B) D.x=A.x-B.x; D.y=A.y-B.y
            Vector2d v1 = start - end;
            Vector2d v2 = p - start;

            double f = v1.X * v2.Y - v2.X * v1.Y;
            return f >= 0;
        }
    }
}
